/*
* Copyright (c) 2012, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.java2dart;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.dart.engine.ast.ArgumentList;
import com.google.dart.engine.ast.AsExpression;
import com.google.dart.engine.ast.AssignmentExpression;
import com.google.dart.engine.ast.AstNode;
import com.google.dart.engine.ast.BinaryExpression;
import com.google.dart.engine.ast.Block;
import com.google.dart.engine.ast.BlockFunctionBody;
import com.google.dart.engine.ast.BooleanLiteral;
import com.google.dart.engine.ast.BreakStatement;
import com.google.dart.engine.ast.CatchClause;
import com.google.dart.engine.ast.ClassDeclaration;
import com.google.dart.engine.ast.ClassMember;
import com.google.dart.engine.ast.Comment;
import com.google.dart.engine.ast.CompilationUnit;
import com.google.dart.engine.ast.CompilationUnitMember;
import com.google.dart.engine.ast.ConstructorDeclaration;
import com.google.dart.engine.ast.ConstructorInitializer;
import com.google.dart.engine.ast.ContinueStatement;
import com.google.dart.engine.ast.Directive;
import com.google.dart.engine.ast.DoubleLiteral;
import com.google.dart.engine.ast.Expression;
import com.google.dart.engine.ast.ExtendsClause;
import com.google.dart.engine.ast.FieldDeclaration;
import com.google.dart.engine.ast.FormalParameter;
import com.google.dart.engine.ast.FormalParameterList;
import com.google.dart.engine.ast.FunctionBody;
import com.google.dart.engine.ast.Identifier;
import com.google.dart.engine.ast.IfStatement;
import com.google.dart.engine.ast.ImplementsClause;
import com.google.dart.engine.ast.InstanceCreationExpression;
import com.google.dart.engine.ast.IntegerLiteral;
import com.google.dart.engine.ast.Label;
import com.google.dart.engine.ast.ListLiteral;
import com.google.dart.engine.ast.MethodDeclaration;
import com.google.dart.engine.ast.MethodInvocation;
import com.google.dart.engine.ast.NodeList;
import com.google.dart.engine.ast.NullLiteral;
import com.google.dart.engine.ast.PrefixedIdentifier;
import com.google.dart.engine.ast.PropertyAccess;
import com.google.dart.engine.ast.ReturnStatement;
import com.google.dart.engine.ast.SimpleFormalParameter;
import com.google.dart.engine.ast.SimpleIdentifier;
import com.google.dart.engine.ast.SimpleStringLiteral;
import com.google.dart.engine.ast.Statement;
import com.google.dart.engine.ast.SuperConstructorInvocation;
import com.google.dart.engine.ast.SuperExpression;
import com.google.dart.engine.ast.ThisExpression;
import com.google.dart.engine.ast.TypeArgumentList;
import com.google.dart.engine.ast.TypeName;
import com.google.dart.engine.ast.TypeParameter;
import com.google.dart.engine.ast.TypeParameterList;
import com.google.dart.engine.ast.VariableDeclaration;
import com.google.dart.engine.ast.VariableDeclarationList;
import com.google.dart.engine.ast.WhileStatement;
import com.google.dart.engine.ast.visitor.ConstantEvaluator;
import com.google.dart.engine.ast.visitor.RecursiveAstVisitor;
import com.google.dart.engine.scanner.Keyword;
import com.google.dart.engine.scanner.StringToken;
import com.google.dart.engine.scanner.Token;
import com.google.dart.engine.scanner.TokenType;
import com.google.dart.java2dart.util.ExecutionUtils;
import com.google.dart.java2dart.util.JavaUtils;
import com.google.dart.java2dart.util.RunnableEx;
import com.google.dart.java2dart.util.ToFormattedSourceVisitor;
import static com.google.dart.java2dart.util.AstFactory.asExpression;
import static com.google.dart.java2dart.util.AstFactory.assertStatement;
import static com.google.dart.java2dart.util.AstFactory.binaryExpression;
import static com.google.dart.java2dart.util.AstFactory.block;
import static com.google.dart.java2dart.util.AstFactory.booleanLiteral;
import static com.google.dart.java2dart.util.AstFactory.breakStatement;
import static com.google.dart.java2dart.util.AstFactory.catchClause;
import static com.google.dart.java2dart.util.AstFactory.classDeclaration;
import static com.google.dart.java2dart.util.AstFactory.compilationUnit;
import static com.google.dart.java2dart.util.AstFactory.conditionalExpression;
import static com.google.dart.java2dart.util.AstFactory.constructorDeclaration;
import static com.google.dart.java2dart.util.AstFactory.declaredIdentifier;
import static com.google.dart.java2dart.util.AstFactory.doStatement;
import static com.google.dart.java2dart.util.AstFactory.doubleLiteral;
import static com.google.dart.java2dart.util.AstFactory.emptyFunctionBody;
import static com.google.dart.java2dart.util.AstFactory.emptyStatement;
import static com.google.dart.java2dart.util.AstFactory.expressionFunctionBody;
import static com.google.dart.java2dart.util.AstFactory.expressionStatement;
import static com.google.dart.java2dart.util.AstFactory.extendsClause;
import static com.google.dart.java2dart.util.AstFactory.fieldDeclaration;
import static com.google.dart.java2dart.util.AstFactory.fieldFormalParameter;
import static com.google.dart.java2dart.util.AstFactory.forEachStatement;
import static com.google.dart.java2dart.util.AstFactory.forStatement;
import static com.google.dart.java2dart.util.AstFactory.formalParameterList;
import static com.google.dart.java2dart.util.AstFactory.identifier;
import static com.google.dart.java2dart.util.AstFactory.ifStatement;
import static com.google.dart.java2dart.util.AstFactory.implementsClause;
import static com.google.dart.java2dart.util.AstFactory.indexExpression;
import static com.google.dart.java2dart.util.AstFactory.instanceCreationExpression;
import static com.google.dart.java2dart.util.AstFactory.integer;
import static com.google.dart.java2dart.util.AstFactory.integerHex;
import static com.google.dart.java2dart.util.AstFactory.isExpression;
import static com.google.dart.java2dart.util.AstFactory.label;
import static com.google.dart.java2dart.util.AstFactory.labeledStatement;
import static com.google.dart.java2dart.util.AstFactory.listLiteral;
import static com.google.dart.java2dart.util.AstFactory.listType;
import static com.google.dart.java2dart.util.AstFactory.methodDeclaration;
import static com.google.dart.java2dart.util.AstFactory.methodInvocation;
import static com.google.dart.java2dart.util.AstFactory.nullLiteral;
import static com.google.dart.java2dart.util.AstFactory.parenthesizedExpression;
import static com.google.dart.java2dart.util.AstFactory.postfixExpression;
import static com.google.dart.java2dart.util.AstFactory.prefixExpression;
import static com.google.dart.java2dart.util.AstFactory.propertyAccess;
import static com.google.dart.java2dart.util.AstFactory.simpleFormalParameter;
import static com.google.dart.java2dart.util.AstFactory.string;
import static com.google.dart.java2dart.util.AstFactory.superConstructorInvocation;
import static com.google.dart.java2dart.util.AstFactory.thisExpression;
import static com.google.dart.java2dart.util.AstFactory.throwExpression;
import static com.google.dart.java2dart.util.AstFactory.tryStatement;
import static com.google.dart.java2dart.util.AstFactory.typeName;
import static com.google.dart.java2dart.util.AstFactory.typeParameter;
import static com.google.dart.java2dart.util.AstFactory.typeParameterList;
import static com.google.dart.java2dart.util.AstFactory.variableDeclaration;
import static com.google.dart.java2dart.util.AstFactory.variableDeclarationList;
import static com.google.dart.java2dart.util.AstFactory.variableDeclarationStatement;
import static com.google.dart.java2dart.util.AstFactory.whileStatement;
import static com.google.dart.java2dart.util.TokenFactory.token;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.SynchronizedStatement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Translates Java AST to Dart AST.
*/
public class SyntaxTranslator extends org.eclipse.jdt.core.dom.ASTVisitor {
private static final Pattern JAVADOC_CODE_PATTERN = Pattern.compile("\\{@code ([^\\}]*)\\}");
private static final Pattern JAVADOC_LINK_PATTERN = Pattern.compile("\\{@link ([^\\}]*)\\}");
/**
* Replaces "node" with "replacement" in parent of "node".
*/
public static void replaceNode(AstNode parent, AstNode node, AstNode replacement) {
Class<? extends AstNode> parentClass = parent.getClass();
// try get/set methods
try {
for (Method getMethod : parentClass.getMethods()) {
String getName = getMethod.getName();
if (getName.startsWith("get") && getMethod.getParameterTypes().length == 0
&& getMethod.invoke(parent) == node) {
String setName = "set" + getName.substring(3);
Method setMethod = parentClass.getMethod(setName, getMethod.getReturnType());
setMethod.invoke(parent, replacement);
return;
}
}
} catch (Throwable e) {
ExecutionUtils.propagate(e);
}
// special cases
if (parent instanceof ListLiteral) {
List<Expression> elements = ((ListLiteral) parent).getElements();
int index = elements.indexOf(node);
if (index != -1) {
elements.set(index, (Expression) replacement);
return;
}
}
if (parent instanceof ArgumentList) {
List<Expression> arguments = ((ArgumentList) parent).getArguments();
int index = arguments.indexOf(node);
if (index != -1) {
arguments.set(index, (Expression) replacement);
return;
}
}
if (parent instanceof FormalParameterList) {
List<FormalParameter> parameters = ((FormalParameterList) parent).getParameters();
int index = parameters.indexOf(node);
if (index != -1) {
parameters.set(index, (FormalParameter) replacement);
return;
}
}
if (parent instanceof TypeArgumentList) {
List<TypeName> arguments = ((TypeArgumentList) parent).getArguments();
int index = arguments.indexOf(node);
if (index != -1) {
arguments.set(index, (TypeName) replacement);
return;
}
}
if (parent instanceof Block) {
Block block = (Block) parent;
NodeList<Statement> statements = block.getStatements();
int index = statements.indexOf(node);
if (index != -1) {
statements.set(index, (Statement) replacement);
return;
}
}
// not found
throw new UnsupportedOperationException("" + parentClass);
}
/**
* Translates given Java AST into Dart AST.
*/
public static CompilationUnit translate(Context context,
org.eclipse.jdt.core.dom.CompilationUnit javaUnit, String javaSource) {
SyntaxTranslator translator = new SyntaxTranslator(context, javaSource, javaUnit);
javaUnit.accept(translator);
return (CompilationUnit) translator.result;
}
static Expression getPrimitiveTypeDefaultValue(String typeName) {
if ("bool".equals(typeName)) {
return booleanLiteral(false);
}
if ("int".equals(typeName)) {
return integer(0);
}
if ("double".equals(typeName)) {
return doubleLiteral(0.0);
}
return null;
}
private static org.eclipse.jdt.core.dom.MethodDeclaration getEnclosingMethod(
org.eclipse.jdt.core.dom.ASTNode node) {
while (node != null) {
if (node instanceof org.eclipse.jdt.core.dom.MethodDeclaration) {
return (org.eclipse.jdt.core.dom.MethodDeclaration) node;
}
node = node.getParent();
}
return null;
}
private static org.eclipse.jdt.core.dom.ITypeBinding getEnclosingTypeBinding(
org.eclipse.jdt.core.dom.ASTNode node) {
while (node != null) {
if (node instanceof org.eclipse.jdt.core.dom.TypeDeclaration) {
return ((org.eclipse.jdt.core.dom.TypeDeclaration) node).resolveBinding();
}
if (node instanceof org.eclipse.jdt.core.dom.AnonymousClassDeclaration) {
return ((org.eclipse.jdt.core.dom.AnonymousClassDeclaration) node).resolveBinding();
}
if (node instanceof org.eclipse.jdt.core.dom.EnumDeclaration) {
return ((org.eclipse.jdt.core.dom.EnumDeclaration) node).resolveBinding();
}
node = node.getParent();
}
return null;
}
/**
* @return the {@link Method} of {@link SyntaxTranslator} to translate
* {@link org.eclipse.jdt.core.dom.ASTNode} of the given class.
*/
private static Method getMostSpecificMethod(Class<?> argumentType) throws Exception {
Method resultMethod = null;
for (Method method : SyntaxTranslator.class.getMethods()) {
if (method.getName().equals("visit")) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1 && parameterTypes[0] == argumentType) {
resultMethod = method;
break;
}
}
}
Assert.isNotNull(resultMethod);
return resultMethod;
}
private static boolean isIntegerType(org.eclipse.jdt.core.dom.Expression expression) {
ITypeBinding typeBinding = expression.resolveTypeBinding();
return JavaUtils.isTypeNamed(typeBinding, "int") || JavaUtils.isTypeNamed(typeBinding, "long")
|| JavaUtils.isTypeNamed(typeBinding, "short");
}
/**
* @return <code>true</code> if "subConstructor" is constructor of inner class which call
* "superConstructor".
*/
private static boolean isSuperConstructor(IMethodBinding superConstructor,
IMethodBinding subConstructor) {
String superString = superConstructor.toString();
String subString = subConstructor.toString();
return superString.endsWith(subString);
}
private final org.eclipse.jdt.core.dom.CompilationUnit javaUnit;
private final Context context;
private final String javaSource;
private AstNode result;
private final List<CompilationUnitMember> artificialUnitDeclarations = Lists.newArrayList();
private MethodDeclaration constructorImpl;
private SyntaxTranslator(Context context, String javaSource,
org.eclipse.jdt.core.dom.CompilationUnit javaUnit) {
this.javaUnit = javaUnit;
this.context = context;
this.javaSource = javaSource;
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.ArrayAccess node) {
Expression expression = translate(node.getArray());
Expression index = translate(node.getIndex());
return done(indexExpression(expression, index));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.ArrayCreation node) {
TypeName listType = translate(node.getType());
TypeArgumentList typeArgs = listType.getTypeArguments();
if (node.getInitializer() != null) {
List<Expression> elements = translateExpressionList(node.getInitializer().expressions());
return done(listLiteral(null, typeArgs, elements));
} else {
List<Expression> arguments = translateArguments(null, node.dimensions());
// may be primitive array element
{
String arrayElementTypeName = typeArgs.getArguments().get(0).getName().getName();
Expression initializer = getPrimitiveTypeDefaultValue(arrayElementTypeName);
if (initializer != null) {
arguments.add(initializer);
return done(instanceCreationExpression(Keyword.NEW, listType, "filled", arguments));
}
}
// non-primitive array element
return done(instanceCreationExpression(Keyword.NEW, listType, arguments));
}
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.ArrayInitializer node) {
List<Expression> elements = translateExpressionList(node.expressions());
return done(listLiteral(elements));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.ArrayType node) {
TypeName elementType = translate(node.getElementType());
int dimensions = node.getDimensions();
return done(listType(elementType, dimensions));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.AssertStatement node) {
return done(assertStatement(translateExpression(node.getExpression())));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.Assignment node) {
Expression left = translate(node.getLeftHandSide());
Expression right = translate(node.getRightHandSide());
// operator
TokenType tokenType = null;
org.eclipse.jdt.core.dom.Assignment.Operator javaOperator = node.getOperator();
if (javaOperator == org.eclipse.jdt.core.dom.Assignment.Operator.ASSIGN) {
tokenType = TokenType.EQ;
}
if (javaOperator == org.eclipse.jdt.core.dom.Assignment.Operator.PLUS_ASSIGN) {
tokenType = TokenType.PLUS_EQ;
}
if (javaOperator == org.eclipse.jdt.core.dom.Assignment.Operator.MINUS_ASSIGN) {
tokenType = TokenType.MINUS_EQ;
}
if (javaOperator == org.eclipse.jdt.core.dom.Assignment.Operator.TIMES_ASSIGN) {
tokenType = TokenType.STAR_EQ;
}
if (javaOperator == org.eclipse.jdt.core.dom.Assignment.Operator.DIVIDE_ASSIGN) {
tokenType = TokenType.SLASH_EQ;
}
if (javaOperator == org.eclipse.jdt.core.dom.Assignment.Operator.REMAINDER_ASSIGN) {
tokenType = TokenType.PERCENT_EQ;
}
if (javaOperator == org.eclipse.jdt.core.dom.Assignment.Operator.LEFT_SHIFT_ASSIGN) {
tokenType = TokenType.LT_LT_EQ;
}
if (javaOperator == org.eclipse.jdt.core.dom.Assignment.Operator.RIGHT_SHIFT_SIGNED_ASSIGN
|| javaOperator == org.eclipse.jdt.core.dom.Assignment.Operator.RIGHT_SHIFT_UNSIGNED_ASSIGN) {
tokenType = TokenType.GT_GT_EQ;
}
if (javaOperator == org.eclipse.jdt.core.dom.Assignment.Operator.BIT_XOR_ASSIGN) {
tokenType = TokenType.CARET_EQ;
}
if (javaOperator == org.eclipse.jdt.core.dom.Assignment.Operator.BIT_OR_ASSIGN) {
tokenType = TokenType.BAR_EQ;
}
if (javaOperator == org.eclipse.jdt.core.dom.Assignment.Operator.BIT_AND_ASSIGN) {
tokenType = TokenType.AMPERSAND_EQ;
}
Assert.isNotNull(tokenType, "No token for: " + javaOperator);
// done
return done(new AssignmentExpression(left, new Token(tokenType, 0), right));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.Block node) {
List<Statement> statements = Lists.newArrayList();
List<org.eclipse.jdt.core.dom.Statement> javaStatements = Lists.newArrayList();
addJavaStatements(javaStatements, node);
for (org.eclipse.jdt.core.dom.Statement javaStatement : javaStatements) {
if (javaStatement instanceof org.eclipse.jdt.core.dom.TypeDeclarationStatement) {
continue;
}
statements.add((Statement) translate(javaStatement));
}
return done(block(statements));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.BooleanLiteral node) {
boolean value = node.booleanValue();
return done(booleanLiteral(value));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.BreakStatement node) {
SimpleIdentifier label = translate(node.getLabel());
return done(breakStatement(label));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.CastExpression node) {
org.eclipse.jdt.core.dom.Type javaType = node.getType();
Expression expression = translate(node.getExpression());
TypeName typeName = translate(javaType);
// (byte) E;
{
String javaTypeName = javaType.toString();
if (javaTypeName.equals("byte")) {
if (expression instanceof IntegerLiteral) {
IntegerLiteral literal = (IntegerLiteral) expression;
return done(integer(literal.getValue().intValue() & 0xFF));
}
return done(methodInvocation("toByte", expression));
}
}
// general case
AsExpression asExpression = asExpression(expression, typeName);
return done(parenthesizedExpression(asExpression));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.CatchClause node) {
SimpleIdentifier exceptionParameter = translateSimpleName(node.getException().getName());
Block block = translate(node.getBody());
// "catch (e) {}" or "on Type catch (e) {}"
Type javaExceptionType = node.getException().getType();
ITypeBinding javaExceptionBinding = javaExceptionType.resolveBinding();
if (JavaUtils.isTypeNamed(javaExceptionBinding, "java.lang.Throwable")
|| JavaUtils.isTypeNamed(javaExceptionBinding, "java.lang.Exception")) {
return done(catchClause(null, exceptionParameter, null, block));
} else {
TypeName exceptionType = translate(javaExceptionType);
return done(catchClause(exceptionType, exceptionParameter, null, block));
}
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.CharacterLiteral node) {
int intValue = node.charValue();
return done(integerHex(intValue));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.ClassInstanceCreation node) {
IMethodBinding binding = node.resolveConstructorBinding();
TypeName typeNameNode = (TypeName) translate(node.getType());
final List<Expression> arguments = translateArguments(binding, node.arguments());
final ClassDeclaration innerClass;
{
AnonymousClassDeclaration anoDeclaration = node.getAnonymousClassDeclaration();
if (anoDeclaration != null) {
String name = typeNameNode.getName().getName().replace('.', '_');
name = name + "_" + context.generateTechnicalAnonymousClassIndex();
innerClass = declareInnerClass(binding, anoDeclaration, name, ArrayUtils.EMPTY_STRING_ARRAY);
typeNameNode = typeName(name);
final SimpleIdentifier typeNameIdentifier = (SimpleIdentifier) typeNameNode.getName();
putReference(binding, typeNameIdentifier);
putReference(binding, innerClass.getName());
// prepare enclosing type
final ITypeBinding enclosingTypeBinding = getEnclosingTypeBinding(node);
final SimpleIdentifier enclosingTypeRef = replaceEnclosingClassMemberReferences(
innerClass,
enclosingTypeBinding);
// declare referenced final variables
anoDeclaration.accept(new ASTVisitor() {
final Set<org.eclipse.jdt.core.dom.IVariableBinding> addedParameters = Sets.newHashSet();
final List<FormalParameter> constructorParameters = Lists.newArrayList();
int index;
@Override
public void endVisit(AnonymousClassDeclaration node) {
if (!constructorParameters.isEmpty()) {
// add parameters to the existing "inner" constructor
for (ClassMember classMember : innerClass.getMembers()) {
if (classMember instanceof ConstructorDeclaration) {
ConstructorDeclaration innerConstructor = (ConstructorDeclaration) classMember;
innerConstructor.getParameters().getParameters().addAll(constructorParameters);
return;
}
}
// create new "inner" constructor
innerClass.getMembers().add(
index,
constructorDeclaration(
typeNameIdentifier,
null,
formalParameterList(constructorParameters),
null));
}
super.endVisit(node);
}
@Override
public void endVisit(SimpleName node) {
IBinding nameBinding = node.resolveBinding();
if (nameBinding instanceof org.eclipse.jdt.core.dom.IVariableBinding) {
org.eclipse.jdt.core.dom.IVariableBinding variableBinding = (org.eclipse.jdt.core.dom.IVariableBinding) nameBinding;
org.eclipse.jdt.core.dom.MethodDeclaration enclosingMethod = getEnclosingMethod(node);
if (!variableBinding.isField() && enclosingMethod != null
&& variableBinding.getDeclaringMethod() != enclosingMethod.resolveBinding()
&& addedParameters.add(variableBinding)) {
TypeName parameterTypeName = translateTypeName(variableBinding.getType());
String parameterName = variableBinding.getName();
SimpleIdentifier parameterNameNode = identifier(parameterName);
innerClass.getMembers().add(
index++,
fieldDeclaration(parameterTypeName, variableDeclaration(parameterNameNode)));
constructorParameters.add(fieldFormalParameter(null, null, parameterNameNode));
arguments.add(parameterNameNode);
context.putReference(parameterNameNode, variableBinding, null);
}
}
super.endVisit(node);
}
@Override
public boolean visit(AnonymousClassDeclaration node) {
if (enclosingTypeRef != null) {
TypeName parameterTypeName = translateTypeName(enclosingTypeBinding);
innerClass.getMembers().add(
index++,
fieldDeclaration(
false,
Keyword.FINAL,
parameterTypeName,
variableDeclaration(enclosingTypeRef)));
constructorParameters.add(fieldFormalParameter(null, null, enclosingTypeRef));
arguments.add(thisExpression());
}
return super.visit(node);
}
});
// replace constructor type with shared (and tracked) identifier
for (ClassMember classMember : innerClass.getMembers()) {
if (classMember instanceof ConstructorDeclaration) {
ConstructorDeclaration cd = (ConstructorDeclaration) classMember;
cd.setReturnType(typeNameIdentifier);
}
}
} else {
innerClass = null;
}
}
InstanceCreationExpression creation = instanceCreationExpression(
Keyword.NEW,
typeNameNode,
null,
arguments);
context.putNodeBinding(creation, binding);
context.putAnonymousDeclaration(creation, innerClass);
context.getConstructorDescription(binding).instanceCreations.add(creation);
return done(creation);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.CompilationUnit node) {
List<Directive> directives = Lists.newArrayList();
List<CompilationUnitMember> declarations = Lists.newArrayList();
for (Iterator<?> I = node.types().iterator(); I.hasNext();) {
Object javaType = I.next();
// skip annotation declarations
if (javaType instanceof org.eclipse.jdt.core.dom.AnnotationTypeDeclaration) {
continue;
}
// translate classes and interfaces
ClassDeclaration dartClass = translate((org.eclipse.jdt.core.dom.ASTNode) javaType);
declarations.add(dartClass);
declarations.addAll(artificialUnitDeclarations);
artificialUnitDeclarations.clear();
}
return done(compilationUnit(directives, declarations));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.ConditionalExpression node) {
return done(conditionalExpression(
translateExpression(node.getExpression()),
translateExpression(node.getThenExpression()),
translateExpression(node.getElseExpression())));
}
/**
* We generate invocation of "impl" method instead of redirecting constructor invocation. The
* reason is that in Java it is possible to have "redirecting constructor invocation" as first
* statement of constructor and then any other statement. But in Dart redirection should be only
* clause.
*/
@Override
public boolean visit(org.eclipse.jdt.core.dom.ConstructorInvocation node) {
IMethodBinding binding = node.resolveConstructorBinding();
SimpleIdentifier nameNode = identifier("thisConstructorRedirection");
// context.getConstructorDescription(binding).implInvocations.add(nameNode);
// invoke "impl"
List<Expression> arguments = translateArguments(binding, node.arguments());
MethodInvocation invocation = methodInvocation(nameNode, arguments);
context.putNodeBinding(invocation, binding);
return done(expressionStatement(invocation));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.ContinueStatement node) {
return done(new ContinueStatement(null, (SimpleIdentifier) translate(node.getLabel()), null));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.DoStatement node) {
return done(doStatement(
(Statement) translate(node.getBody()),
translateExpression(node.getExpression())));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.EmptyStatement node) {
return done(emptyStatement());
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.EnhancedForStatement node) {
SimpleFormalParameter sfp = (SimpleFormalParameter) translate(node.getParameter());
return done(forEachStatement(
declaredIdentifier(sfp.getType(), sfp.getIdentifier()),
translateExpression(node.getExpression()),
(Statement) translate(node.getBody())));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.EnumConstantDeclaration node) {
String fieldName = node.getName().getIdentifier();
IMethodBinding constructorBinding = node.resolveConstructorBinding();
// prepare enum name
org.eclipse.jdt.core.dom.EnumDeclaration parentEnum = (org.eclipse.jdt.core.dom.EnumDeclaration) node.getParent();
String enumTypeName = parentEnum.getName().getIdentifier();
// may be create Dart top-level class for Java inner class
String innerClassName = null;
{
AnonymousClassDeclaration anoClassDeclaration = node.getAnonymousClassDeclaration();
if (anoClassDeclaration != null) {
innerClassName = enumTypeName + "_" + fieldName;
declareInnerClass(constructorBinding, anoClassDeclaration, innerClassName, new String[] {
"String", "name", "int", "ordinal"});
}
}
// prepare field type
TypeName type = typeName(enumTypeName);
// prepare field variables
List<VariableDeclaration> variables = Lists.newArrayList();
{
List<Expression> argList = translateArguments(null, node.arguments());
{
int ordinal = parentEnum.enumConstants().indexOf(node);
argList.add(0, integer(ordinal));
argList.add(0, string(fieldName));
}
InstanceCreationExpression init;
if (innerClassName == null) {
init = instanceCreationExpression(Keyword.CONST, typeName(enumTypeName), argList);
context.getConstructorDescription(constructorBinding).instanceCreations.add(init);
} else {
init = instanceCreationExpression(Keyword.CONST, typeName(innerClassName), argList);
}
variables.add(variableDeclaration(fieldName, init));
}
return done(fieldDeclaration(translateJavadoc(node), true, Keyword.CONST, type, variables));
}
@Override
@SuppressWarnings("unchecked")
public boolean visit(org.eclipse.jdt.core.dom.EnumDeclaration node) {
SimpleIdentifier name = translateSimpleName(node.getName());
// extends
ExtendsClause extendsClause = extendsClause(typeName("Enum", typeName(name)));
// implements
ImplementsClause implementsClause = null;
{
List<TypeName> interfaces = Lists.newArrayList();
if (!node.superInterfaceTypes().isEmpty()) {
for (Object javaInterface : node.superInterfaceTypes()) {
interfaces.add((TypeName) translate((org.eclipse.jdt.core.dom.ASTNode) javaInterface));
}
implementsClause = new ImplementsClause(null, interfaces);
}
}
// members
List<ClassMember> members = Lists.newArrayList();
{
// constants
List<Expression> valuesList = Lists.newArrayList();
for (Object javaConst : node.enumConstants()) {
org.eclipse.jdt.core.dom.EnumConstantDeclaration javaEnumConst = (org.eclipse.jdt.core.dom.EnumConstantDeclaration) javaConst;
members.add((FieldDeclaration) translate(javaEnumConst));
valuesList.add(identifier(javaEnumConst.getName().getIdentifier()));
}
// values
members.add(fieldDeclaration(
true,
Keyword.CONST,
listType(typeName(name), 1),
variableDeclaration("values", listLiteral(Keyword.CONST, null, valuesList))));
// body declarations
boolean hasConstructor = false;
for (Iterator<?> I = node.bodyDeclarations().iterator(); I.hasNext();) {
org.eclipse.jdt.core.dom.BodyDeclaration javaBodyDecl = (org.eclipse.jdt.core.dom.BodyDeclaration) I.next();
constructorImpl = null;
ClassMember member = translate(javaBodyDecl);
members.add(member);
if (constructorImpl != null) {
members.add(constructorImpl);
}
if (javaBodyDecl instanceof org.eclipse.jdt.core.dom.MethodDeclaration) {
if (((org.eclipse.jdt.core.dom.MethodDeclaration) javaBodyDecl).isConstructor()) {
hasConstructor = true;
}
}
}
// add default constructor, use artificial constructor
if (!hasConstructor) {
org.eclipse.jdt.core.dom.MethodDeclaration ac = node.getAST().newMethodDeclaration();
try {
ac.setConstructor(true);
ac.setName(node.getAST().newSimpleName(name.getName()));
ac.setBody(node.getAST().newBlock());
node.bodyDeclarations().add(ac);
ConstructorDeclaration innerConstructor = translate(ac);
members.add(innerConstructor);
if (constructorImpl != null) {
members.add(constructorImpl);
}
} finally {
node.bodyDeclarations().remove(ac);
}
}
}
return done(classDeclaration(
translateJavadoc(node),
name,
extendsClause,
null,
implementsClause,
members));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.ExpressionStatement node) {
Expression expression = translate(node.getExpression());
return done(expressionStatement(expression));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.FieldAccess node) {
PropertyAccess result = propertyAccess(
translateExpression(node.getExpression()),
(SimpleIdentifier) translate(node.getName()));
context.putNodeBinding(result, node.resolveFieldBinding());
return done(result);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.FieldDeclaration node) {
boolean isPrivate = JavaUtils.isPrivate(node) || JavaUtils.isPackagePrivate(node);
boolean isStatic = org.eclipse.jdt.core.dom.Modifier.isStatic(node.getModifiers());
boolean isFinal = false;
// interface field
org.eclipse.jdt.core.dom.ASTNode parent = node.getParent();
if (parent instanceof TypeDeclaration && ((TypeDeclaration) parent).isInterface()) {
isPrivate = false;
isStatic = true;
isFinal = true;
}
// create node
FieldDeclaration fieldDeclaration = fieldDeclaration(
translateJavadoc(node),
isStatic,
translateVariableDeclarationList(isFinal, node.getType(), node.fragments()));
if (isPrivate) {
context.putPrivateClassMember(fieldDeclaration);
}
translateAnnotations(fieldDeclaration, node.modifiers());
return done(fieldDeclaration);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.ForStatement node) {
Expression condition = translateExpression(node.getExpression());
List<Expression> updaters = translateExpressionList(node.updaters());
Statement body = (Statement) translate(node.getBody());
Object javaInitializer = !node.initializers().isEmpty() ? node.initializers().get(0) : null;
if (javaInitializer instanceof org.eclipse.jdt.core.dom.VariableDeclarationExpression) {
org.eclipse.jdt.core.dom.VariableDeclarationExpression javaVDE = (org.eclipse.jdt.core.dom.VariableDeclarationExpression) javaInitializer;
List<VariableDeclaration> variables = Lists.newArrayList();
for (Iterator<?> I = javaVDE.fragments().iterator(); I.hasNext();) {
org.eclipse.jdt.core.dom.VariableDeclarationFragment fragment = (org.eclipse.jdt.core.dom.VariableDeclarationFragment) I.next();
variables.add((VariableDeclaration) translate(fragment));
}
VariableDeclarationList variableList = variableDeclarationList(
null,
(TypeName) translate(javaVDE.getType()),
variables);
return done(forStatement(variableList, condition, updaters, body));
} else {
Expression initializer = translate((org.eclipse.jdt.core.dom.ASTNode) javaInitializer);
return done(forStatement(initializer, condition, updaters, body));
}
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.IfStatement node) {
return done(ifStatement(
translateExpression(node.getExpression()),
(Statement) translate(node.getThenStatement()),
(Statement) translate(node.getElseStatement())));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.InfixExpression node) {
Expression left = translate(node.getLeftOperand());
Expression right = translate(node.getRightOperand());
// operator
TokenType tokenType = null;
org.eclipse.jdt.core.dom.InfixExpression.Operator javaOperator = node.getOperator();
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.PLUS) {
tokenType = TokenType.PLUS;
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.MINUS) {
tokenType = TokenType.MINUS;
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.TIMES) {
tokenType = TokenType.STAR;
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.DIVIDE) {
if (isIntegerType(node.getLeftOperand()) && isIntegerType(node.getRightOperand())) {
tokenType = TokenType.TILDE_SLASH;
} else {
tokenType = TokenType.SLASH;
}
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.REMAINDER) {
tokenType = TokenType.PERCENT;
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.LEFT_SHIFT) {
tokenType = TokenType.LT_LT;
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.RIGHT_SHIFT_SIGNED
|| javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED) {
tokenType = TokenType.GT_GT;
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.CONDITIONAL_OR) {
tokenType = TokenType.BAR_BAR;
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.CONDITIONAL_AND) {
tokenType = TokenType.AMPERSAND_AMPERSAND;
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.XOR) {
tokenType = TokenType.CARET;
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.OR) {
tokenType = TokenType.BAR;
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.AND) {
tokenType = TokenType.AMPERSAND;
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.LESS) {
tokenType = TokenType.LT;
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.GREATER) {
tokenType = TokenType.GT;
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.LESS_EQUALS) {
tokenType = TokenType.LT_EQ;
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.GREATER_EQUALS) {
tokenType = TokenType.GT_EQ;
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.EQUALS) {
if (isNumberOrNull(left) || isNumberOrNull(right) || isEnum(left) && isEnum(right)) {
tokenType = TokenType.EQ_EQ;
} else {
return done(methodInvocation("identical", left, right));
}
}
if (javaOperator == org.eclipse.jdt.core.dom.InfixExpression.Operator.NOT_EQUALS) {
if (isNumberOrNull(left) || isNumberOrNull(right) || isEnum(left) && isEnum(right)) {
tokenType = TokenType.BANG_EQ;
} else {
return done(prefixExpression(TokenType.BANG, methodInvocation("identical", left, right)));
}
}
Assert.isNotNull(tokenType, "No token for: " + javaOperator);
// create BinaryExpression
BinaryExpression binary = binaryExpression(left, tokenType, right);
for (Object javaOperand : node.extendedOperands()) {
context.putNodeTypeBinding(binary, node.resolveTypeBinding());
Expression operand = translate((org.eclipse.jdt.core.dom.ASTNode) javaOperand);
binary = binaryExpression(binary, tokenType, operand);
}
return done(binary);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.InstanceofExpression node) {
return done(isExpression(
translateExpression(node.getLeftOperand()),
false,
(TypeName) translate(node.getRightOperand())));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.Javadoc node) {
String javaDocString = getJavaSource(node);
// there is some one-off sometimes, probably bug in JDT
if (!javaDocString.startsWith("/**")) {
if (javaDocString.startsWith("**")) {
javaDocString = "/" + javaDocString.trim();
} else {
javaDocString = "/**\n" + javaDocString.trim();
}
}
String dartDocString = convertJavaDoc(javaDocString);
StringToken commentToken = new StringToken(TokenType.STRING, dartDocString, 0);
return done(Comment.createDocumentationComment(new Token[] {commentToken}));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.LabeledStatement node) {
List<Label> labels = Lists.newArrayList();
while (true) {
SimpleIdentifier labelIdentifier = translate(node.getLabel());
labels.add(label(labelIdentifier));
if (node.getBody() instanceof org.eclipse.jdt.core.dom.LabeledStatement) {
node = (org.eclipse.jdt.core.dom.LabeledStatement) node.getBody();
} else {
break;
}
}
return done(labeledStatement(labels, (Statement) translate(node.getBody())));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.MethodDeclaration node) {
boolean isPrivate = JavaUtils.isPrivate(node) || JavaUtils.isPackagePrivate(node);
IMethodBinding binding = node.resolveBinding();
// parameters
FormalParameterList parameterList = translateMethodDeclarationParameters(node);
// body
FunctionBody body;
SuperConstructorInvocation superConstructorInvocation = null;
{
org.eclipse.jdt.core.dom.Block javaBlock = node.getBody();
if (javaBlock != null) {
for (Object javaStatement : javaBlock.statements()) {
if (javaStatement instanceof org.eclipse.jdt.core.dom.SuperConstructorInvocation) {
superConstructorInvocation = translate((org.eclipse.jdt.core.dom.SuperConstructorInvocation) javaStatement);
}
}
Block bodyBlock = (Block) translate(javaBlock);
body = new BlockFunctionBody(null, null, bodyBlock);
List<Statement> statements = bodyBlock.getStatements();
// convert "{ return foo; }" to "=> foo;"
if (statements.size() == 1 && statements.get(0) instanceof ReturnStatement) {
body = expressionFunctionBody(((ReturnStatement) statements.get(0)).getExpression());
}
} else {
body = emptyFunctionBody();
}
}
// constructor
if (node.isConstructor()) {
return translateMethodDeclarationConstructor(
node,
binding,
parameterList,
body,
superConstructorInvocation);
} else {
boolean isStatic = org.eclipse.jdt.core.dom.Modifier.isStatic(node.getModifiers());
SimpleIdentifier dartMethodName = translateSimpleName(node.getName());
MethodDeclaration methodDeclaration = methodDeclaration(
translateJavadoc(node),
isStatic,
(TypeName) translate(node.getReturnType2()),
dartMethodName,
parameterList,
body);
context.putNodeBinding(methodDeclaration, binding);
if (isPrivate) {
context.putPrivateClassMember(methodDeclaration);
}
translateAnnotations(methodDeclaration, node.modifiers());
return done(methodDeclaration);
}
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.MethodInvocation node) {
IMethodBinding binding = node.resolveMethodBinding();
Expression target = translateExpression(node.getExpression());
List<Expression> arguments = translateArguments(binding, node.arguments());
// prepare invocation
Identifier name = translate(node.getName());
MethodInvocation invocation;
if (name instanceof SimpleIdentifier) {
invocation = methodInvocation(target, (SimpleIdentifier) name, arguments);
} else {
PrefixedIdentifier prefixedName = (PrefixedIdentifier) name;
target = prefixedName.getPrefix();
invocation = methodInvocation(target, prefixedName.getIdentifier(), arguments);
}
// done
context.putNodeBinding(invocation, binding);
return done(invocation);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.NullLiteral node) {
return done(nullLiteral());
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.NumberLiteral node) {
String token = node.getToken();
if (token.contains(".")
|| !StringUtils.startsWithIgnoreCase(token, "0x")
&& (StringUtils.endsWithIgnoreCase(token, "F") || StringUtils.endsWithIgnoreCase(token, "D"))) {
token = StringUtils.removeEndIgnoreCase(token, "F");
token = StringUtils.removeEndIgnoreCase(token, "D");
if (!token.contains(".")) {
token += ".0";
}
return done(new DoubleLiteral(token(TokenType.DOUBLE, token), 0));
} else {
token = StringUtils.removeEndIgnoreCase(token, "L");
long value;
if (token.startsWith("0x")) {
value = Long.parseLong(token.substring(2), 16);
} else {
value = Long.parseLong(token);
}
return done(new IntegerLiteral(token(TokenType.INT, token), BigInteger.valueOf(value)));
}
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.ParameterizedType node) {
List<TypeName> typeArguments;
{
List<?> javaTypeArguments = node.typeArguments();
boolean hasMethodTypeVariable = false;
for (Object _javaTypeArgument : javaTypeArguments) {
org.eclipse.jdt.core.dom.Type javaTypeArgument = (org.eclipse.jdt.core.dom.Type) _javaTypeArgument;
ITypeBinding binding = javaTypeArgument.resolveBinding();
if (binding != null && binding.isTypeVariable() && binding.getDeclaringMethod() != null) {
hasMethodTypeVariable = true;
break;
}
}
if (hasMethodTypeVariable) {
typeArguments = null;
} else {
typeArguments = translateTypeNames(javaTypeArguments);
}
}
// may be all dynamic type arguments
if (typeArguments != null) {
boolean allDynamicTypeArgs = true;
for (TypeName typeName : typeArguments) {
allDynamicTypeArgs &= typeName.getName().getName().equals("dynamic");
}
if (allDynamicTypeArgs) {
typeArguments = null;
}
}
// continue
ITypeBinding binding = node.resolveBinding();
TypeName typeName = typeName(((TypeName) translate(node.getType())).getName(), typeArguments);
context.putNodeBinding(typeName, binding);
context.putNodeTypeBinding(typeName, binding);
return done(typeName);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.ParenthesizedExpression node) {
Expression expression = translate(node.getExpression());
return done(parenthesizedExpression(expression));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.PostfixExpression node) {
Expression operand = translate(node.getOperand());
// operator
TokenType tokenType = null;
org.eclipse.jdt.core.dom.PostfixExpression.Operator javaOperator = node.getOperator();
if (javaOperator == org.eclipse.jdt.core.dom.PostfixExpression.Operator.INCREMENT) {
tokenType = TokenType.PLUS_PLUS;
}
if (javaOperator == org.eclipse.jdt.core.dom.PostfixExpression.Operator.DECREMENT) {
tokenType = TokenType.MINUS_MINUS;
}
// done
return done(postfixExpression(operand, tokenType));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.PrefixExpression node) {
Expression operand = translate(node.getOperand());
// operator
TokenType tokenType = null;
org.eclipse.jdt.core.dom.PrefixExpression.Operator javaOperator = node.getOperator();
if (javaOperator == org.eclipse.jdt.core.dom.PrefixExpression.Operator.PLUS) {
return done(operand);
}
if (javaOperator == org.eclipse.jdt.core.dom.PrefixExpression.Operator.INCREMENT) {
tokenType = TokenType.PLUS_PLUS;
}
if (javaOperator == org.eclipse.jdt.core.dom.PrefixExpression.Operator.DECREMENT) {
tokenType = TokenType.MINUS_MINUS;
}
if (javaOperator == org.eclipse.jdt.core.dom.PrefixExpression.Operator.MINUS) {
tokenType = TokenType.MINUS;
}
if (javaOperator == org.eclipse.jdt.core.dom.PrefixExpression.Operator.NOT) {
tokenType = TokenType.BANG;
}
if (javaOperator == org.eclipse.jdt.core.dom.PrefixExpression.Operator.COMPLEMENT) {
tokenType = TokenType.TILDE;
}
Assert.isNotNull(tokenType, "No token for: " + javaOperator);
// done
return done(prefixExpression(tokenType, operand));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.PrimitiveType node) {
String name = node.toString();
ITypeBinding binding = node.resolveBinding();
if ("boolean".equals(name)) {
name = "bool";
}
if ("byte".equals(name) || "char".equals(name) || "short".equals(name) || "long".equals(name)) {
name = "int";
}
if ("float".equals(name)) {
name = "double";
}
TypeName typeName = typeName(name);
context.putNodeTypeBinding(typeName, binding);
return done(typeName);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.QualifiedName node) {
PropertyAccess result = propertyAccess(
translateExpression(node.getQualifier()),
translateSimpleName(node.getName()));
context.putNodeBinding(result, node.resolveBinding());
return done(result);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.ReturnStatement node) {
return done(new ReturnStatement(null, translateExpression(node.getExpression()), null));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.SimpleName node) {
IBinding binding = node.resolveBinding();
SimpleIdentifier result = identifier(node.getIdentifier());
putReference(binding, result);
// may be statically imported field, generate PropertyAccess
{
org.eclipse.jdt.core.dom.StructuralPropertyDescriptor locationInParent = node.getLocationInParent();
if (binding instanceof IVariableBinding) {
org.eclipse.jdt.core.dom.IVariableBinding variableBinding = (org.eclipse.jdt.core.dom.IVariableBinding) binding;
org.eclipse.jdt.core.dom.ASTNode parent = node.getParent();
if (locationInParent == org.eclipse.jdt.core.dom.EnumConstantDeclaration.ARGUMENTS_PROPERTY
|| locationInParent == org.eclipse.jdt.core.dom.ClassInstanceCreation.ARGUMENTS_PROPERTY
|| locationInParent == org.eclipse.jdt.core.dom.MethodInvocation.ARGUMENTS_PROPERTY
|| locationInParent == org.eclipse.jdt.core.dom.ConstructorInvocation.ARGUMENTS_PROPERTY
|| locationInParent == org.eclipse.jdt.core.dom.SuperConstructorInvocation.ARGUMENTS_PROPERTY
|| locationInParent == org.eclipse.jdt.core.dom.Assignment.RIGHT_HAND_SIDE_PROPERTY
|| locationInParent == org.eclipse.jdt.core.dom.SwitchCase.EXPRESSION_PROPERTY
|| parent instanceof org.eclipse.jdt.core.dom.InfixExpression
|| parent instanceof org.eclipse.jdt.core.dom.ConditionalExpression
|| parent instanceof org.eclipse.jdt.core.dom.ReturnStatement) {
ITypeBinding declaringBinding = variableBinding.getDeclaringClass();
ITypeBinding enclosingBinding = getEnclosingTypeBinding(node);
if (declaringBinding != null && enclosingBinding != declaringBinding
&& org.eclipse.jdt.core.dom.Modifier.isStatic(variableBinding.getModifiers())) {
SimpleIdentifier target = identifier(declaringBinding.getName());
putReference(declaringBinding, target);
return done(propertyAccess(target, result));
}
}
}
}
// may be statically imported method, generate PrefixedIdentifier
{
org.eclipse.jdt.core.dom.StructuralPropertyDescriptor locationInParent = node.getLocationInParent();
if (binding instanceof IMethodBinding) {
IMethodBinding methodBinding = (IMethodBinding) binding;
if (locationInParent == org.eclipse.jdt.core.dom.MethodInvocation.NAME_PROPERTY
&& ((org.eclipse.jdt.core.dom.MethodInvocation) node.getParent()).getExpression() == null) {
ITypeBinding declaringBinding = methodBinding.getDeclaringClass();
ITypeBinding enclosingBinding = getEnclosingTypeBinding(node);
if (declaringBinding != null && enclosingBinding != declaringBinding
&& org.eclipse.jdt.core.dom.Modifier.isStatic(methodBinding.getModifiers())) {
SimpleIdentifier prefix = identifier(declaringBinding.getName());
putReference(declaringBinding, prefix);
return done(identifier(prefix, result));
}
}
}
}
// done
return done(result);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.SimpleType node) {
String name = node.getName().toString();
ITypeBinding binding = node.resolveBinding();
// in Dart we cannot use separate type parameters for methods, so we replace
// them with type bounds
if (binding != null && binding.isTypeVariable() && binding.getDeclaringMethod() != null) {
binding = binding.getErasure();
name = binding.getName();
}
// translate name
SimpleIdentifier nameNode = identifier(name);
putReference(binding, nameNode);
{
if ("Void".equals(name)) {
nameNode = identifier("Object");
}
if ("Boolean".equals(name)) {
nameNode = identifier("bool");
}
if ("Number".equals(name)) {
nameNode = identifier("num");
}
if ("Short".equals(name) || "Integer".equals(name) || "Long".equals(name)) {
nameNode = identifier("int");
}
if ("Float".equals(name) || "Double".equals(name)) {
nameNode = identifier("double");
}
if ("BigInteger".equals(name)) {
nameNode = identifier("int");
}
}
// done
TypeName typeName = typeName(nameNode);
context.putNodeBinding(typeName, binding);
context.putNodeTypeBinding(typeName, binding);
return done(typeName);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.SingleVariableDeclaration node) {
TypeName type = (TypeName) translate(node.getType());
type = listType(type, node.getExtraDimensions());
if (node.isVarargs()) {
type = listType(type, 1);
}
return done(simpleFormalParameter(type, translateSimpleName(node.getName())));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.StringLiteral node) {
String tokenValue = node.getEscapedValue();
tokenValue = StringUtils.replace(tokenValue, "$", "\\$");
SimpleStringLiteral literal = new SimpleStringLiteral(
token(TokenType.STRING, tokenValue),
node.getLiteralValue());
context.putNodeTypeBinding(literal, node.resolveTypeBinding());
return done(literal);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.SuperConstructorInvocation node) {
IMethodBinding binding = node.resolveConstructorBinding();
// invoke "impl"
List<Expression> arguments = translateArguments(binding, node.arguments());
SuperConstructorInvocation superInvocation = superConstructorInvocation(arguments);
context.getConstructorDescription(binding).superInvocations.add(superInvocation);
context.putNodeBinding(superInvocation, binding);
return done(superInvocation);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.SuperMethodInvocation node) {
IMethodBinding binding = node.resolveMethodBinding();
Expression target = new SuperExpression(null);
List<Expression> arguments = translateArguments(binding, node.arguments());
SimpleIdentifier name = translateSimpleName(node.getName());
return done(methodInvocation(target, name, arguments));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.SwitchStatement node) {
IfStatement mainIfStatement = null;
IfStatement targetIfStatement = null;
Block ifBlock = null;
Expression ifCondition = null;
for (Iterator<?> I = node.statements().iterator(); I.hasNext();) {
Object javaMember = I.next();
if (javaMember instanceof org.eclipse.jdt.core.dom.SwitchCase) {
org.eclipse.jdt.core.dom.SwitchCase javaCase = (org.eclipse.jdt.core.dom.SwitchCase) javaMember;
if (javaCase.getExpression() != null) {
Expression condition = binaryExpression(
translateExpression(node.getExpression()),
TokenType.EQ_EQ,
translateExpression(javaCase.getExpression()));
if (ifCondition == null) {
ifCondition = condition;
ifBlock = block();
IfStatement ifStatement = ifStatement(condition, ifBlock);
if (mainIfStatement == null) {
mainIfStatement = ifStatement;
} else {
targetIfStatement.setElseStatement(ifStatement);
}
targetIfStatement = ifStatement;
} else {
ifCondition = binaryExpression(ifCondition, TokenType.BAR_BAR, condition);
targetIfStatement.setCondition(ifCondition);
}
} else {
ifBlock = block();
targetIfStatement.setElseStatement(ifBlock);
}
} else {
ifCondition = null;
Statement statement = translate((org.eclipse.jdt.core.dom.Statement) javaMember);
if (!(statement instanceof BreakStatement)) {
ifBlock.getStatements().add(statement);
}
}
}
// wrap everything into "while(true)" to handle inner "break"
WhileStatement whileStatement = whileStatement(
booleanLiteral(true),
block(mainIfStatement, breakStatement()));
return done(whileStatement);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.ThisExpression node) {
ITypeBinding binding = node.resolveTypeBinding();
ThisExpression thisExpression = thisExpression();
context.putNodeBinding(thisExpression, binding);
context.putNodeTypeBinding(thisExpression, binding);
return done(thisExpression);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.ThrowStatement node) {
return done(expressionStatement(throwExpression(translateExpression(node.getExpression()))));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.TryStatement node) {
List<CatchClause> catchClauses = Lists.newArrayList();
for (Iterator<?> I = node.catchClauses().iterator(); I.hasNext();) {
org.eclipse.jdt.core.dom.CatchClause javaCatch = (org.eclipse.jdt.core.dom.CatchClause) I.next();
catchClauses.add((CatchClause) translate(javaCatch));
}
return done(tryStatement(
(Block) translate(node.getBody()),
catchClauses,
(Block) translate(node.getFinally())));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.TypeDeclaration node) {
ITypeBinding binding = node.resolveBinding();
ITypeBinding enclosingTypeBinding = binding != null ? binding.getDeclaringClass() : null;
// prepare name
SimpleIdentifier name;
{
name = translateSimpleName(node.getName());
if (enclosingTypeBinding != null) {
context.putInnerClassName(name);
name.setToken(token(
TokenType.IDENTIFIER,
enclosingTypeBinding.getName() + "_" + name.getName()));
}
}
// interface
Token abstractToken = null;
if (node.isInterface() || org.eclipse.jdt.core.dom.Modifier.isAbstract(node.getModifiers())) {
abstractToken = token(Keyword.ABSTRACT);
}
// type parameters
TypeParameterList typeParams = null;
{
List<TypeParameter> typeParameters = Lists.newArrayList();
List<?> javaTypeParameters = node.typeParameters();
if (!javaTypeParameters.isEmpty()) {
for (Iterator<?> I = javaTypeParameters.iterator(); I.hasNext();) {
org.eclipse.jdt.core.dom.TypeParameter javaTypeParameter = (org.eclipse.jdt.core.dom.TypeParameter) I.next();
TypeParameter typeParameter = translate(javaTypeParameter);
typeParameters.add(typeParameter);
}
typeParams = typeParameterList(typeParameters);
}
}
// extends
ExtendsClause extendsClause = null;
if (node.getSuperclassType() != null) {
TypeName superType = translate(node.getSuperclassType());
extendsClause = extendsClause(superType);
}
// implements
ImplementsClause implementsClause = null;
if (!node.superInterfaceTypes().isEmpty()) {
List<TypeName> interfaces = Lists.newArrayList();
for (Object javaInterface : node.superInterfaceTypes()) {
interfaces.add((TypeName) translate((org.eclipse.jdt.core.dom.ASTNode) javaInterface));
}
implementsClause = implementsClause(interfaces);
}
// members
List<ClassMember> members = translateBodyDeclarations(node.bodyDeclarations());
for (ClassMember member : members) {
if (member instanceof ConstructorDeclaration) {
ConstructorDeclaration constructor = (ConstructorDeclaration) member;
constructor.setReturnType(name);
}
}
//
ClassDeclaration classDeclaration = new ClassDeclaration(
translateJavadoc(node),
null,
abstractToken,
null,
name,
typeParams,
extendsClause,
null,
implementsClause,
null,
members,
null);
context.putNodeBinding(classDeclaration, binding);
context.putNodeTypeBinding(classDeclaration, binding);
context.putNodeTypeBinding(name, binding);
// may be insert enclosing type reference
SimpleIdentifier enclosingTypeRef = replaceEnclosingClassMemberReferences(
classDeclaration,
enclosingTypeBinding);
if (enclosingTypeRef != null) {
TypeName enclosingTypeName = translateTypeName(enclosingTypeBinding);
classDeclaration.getMembers().add(
0,
fieldDeclaration(
false,
Keyword.FINAL,
enclosingTypeName,
variableDeclaration(enclosingTypeRef)));
boolean hasConstructors = false;
for (ClassMember member : members) {
if (member instanceof ConstructorDeclaration) {
hasConstructors = true;
ConstructorDeclaration constructor = (ConstructorDeclaration) member;
constructor.getParameters().getParameters().add(
0,
fieldFormalParameter(null, null, enclosingTypeRef));
context.getConstructorDescription(constructor).insertEnclosingTypeRef = true;
}
}
// no constructors, add default one
if (!hasConstructors) {
ConstructorDeclaration constructor = constructorDeclaration(
name,
null,
formalParameterList(fieldFormalParameter(null, null, enclosingTypeRef)),
null);
boolean addedConstructor = false;
members = classDeclaration.getMembers();
for (int i = 0; i < members.size(); i++) {
ClassMember classMember = members.get(i);
if (!(classMember instanceof FieldDeclaration)) {
addedConstructor = true;
members.add(i, constructor);
break;
}
}
if (!addedConstructor) {
members.add(constructor);
}
// register generated default constructor with its binding
{
IMethodBinding[] javaMethodBindings = binding.getDeclaredMethods();
for (IMethodBinding javaMethodBinding : javaMethodBindings) {
if (javaMethodBinding.isConstructor()) {
context.putConstructorBinding(constructor, javaMethodBinding);
break;
}
}
}
// mark: add enclosing instance argument
context.getConstructorDescription(constructor).insertEnclosingTypeRef = true;
}
}
// done
translateAnnotations(classDeclaration, node.modifiers());
return done(classDeclaration);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.TypeLiteral node) {
org.eclipse.jdt.core.dom.Type javaType = node.getType();
Identifier result = null;
if (javaType instanceof org.eclipse.jdt.core.dom.SimpleType) {
result = ((TypeName) translate(javaType)).getName();
} else {
result = identifier("CannotTranslate");
}
return done(result);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.TypeParameter node) {
SimpleIdentifier name = translateSimpleName(node.getName());
TypeName bound = null;
{
List<?> typeBounds = node.typeBounds();
if (typeBounds.size() == 1) {
org.eclipse.jdt.core.dom.Type javaBound = (org.eclipse.jdt.core.dom.Type) typeBounds.get(0);
bound = (TypeName) translate(javaBound);
}
}
return done(typeParameter(name, bound));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.VariableDeclarationFragment node) {
VariableDeclaration varDecl = variableDeclaration(
translateSimpleName(node.getName()),
translateExpression(node.getInitializer()));
{
IVariableBinding binding = node.resolveBinding();
if (binding != null) {
context.putNodeTypeBinding(varDecl, binding.getType());
}
}
return done(varDecl);
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.VariableDeclarationStatement node) {
return done(variableDeclarationStatement(translateVariableDeclarationList(
false,
node.getType(),
node.fragments())));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.WhileStatement node) {
return done(whileStatement(
translateExpression(node.getExpression()),
(Statement) translate(node.getBody())));
}
@Override
public boolean visit(org.eclipse.jdt.core.dom.WildcardType node) {
org.eclipse.jdt.core.dom.Type javaBoundType = node.getBound();
if (javaBoundType == null) {
return done(typeName("dynamic"));
} else {
return done(translate(javaBoundType));
}
}
protected String convertJavaDoc(String str) {
// <p> ==> blank lines (which get interpreted as <br>
str = str.replaceAll("<p>", "");
// {@code foo} ==> `foo`
Matcher matcher = JAVADOC_CODE_PATTERN.matcher(str);
while (matcher.find()) {
str = str.substring(0, matcher.start()) + "`" + matcher.group(1) + "`"
+ str.substring(matcher.end());
matcher = JAVADOC_CODE_PATTERN.matcher(str);
}
// {@link #getPrefixes()} ==> [getPrefixes]
matcher = JAVADOC_LINK_PATTERN.matcher(str);
while (matcher.find()) {
str = str.substring(0, matcher.start()) + "[" + extractLinkReference(matcher.group(1)) + "]"
+ str.substring(matcher.end());
matcher = JAVADOC_LINK_PATTERN.matcher(str);
}
// <ul>, </ul>
str = deleteLinesContaining(str, "<ul>", false);
str = deleteLinesContaining(str, "</ul>", false);
// <li>
str = str.replaceAll("<li>", "* ").replaceAll("</li>", "");
// @coverage
str = deleteLinesContaining(str, "@coverage", true);
return str;
}
/**
* Adds Java statements of the given Java block. Unrolls {@link SynchronizedStatement}s.
*/
private void addJavaStatements(List<org.eclipse.jdt.core.dom.Statement> statements,
org.eclipse.jdt.core.dom.Block block) {
for (Iterator<?> I = block.statements().iterator(); I.hasNext();) {
org.eclipse.jdt.core.dom.Statement javaStatement = (org.eclipse.jdt.core.dom.Statement) I.next();
if (javaStatement instanceof org.eclipse.jdt.core.dom.SuperConstructorInvocation) {
continue;
}
if (javaStatement instanceof org.eclipse.jdt.core.dom.SynchronizedStatement) {
addJavaStatements(
statements,
((org.eclipse.jdt.core.dom.SynchronizedStatement) javaStatement).getBody());
continue;
}
statements.add(javaStatement);
}
}
private int backupOverBlankLine(String string, int first) {
int index = first - 1;
if (string.charAt(index) == '\r' && string.charAt(first) == '\n') {
index--;
}
char currentChar = string.charAt(index);
while (!isEol(currentChar)) {
if (!Character.isWhitespace(currentChar) && currentChar != '*') {
return first;
}
currentChar = string.charAt(--index);
}
return index;
}
private ClassDeclaration declareInnerClass(IMethodBinding constructorBinding,
AnonymousClassDeclaration anoClassDeclaration, String innerClassName,
String[] additionalParameters) {
ITypeBinding superTypeBinding = anoClassDeclaration.resolveBinding().getSuperclass();
ExtendsClause extendsClause = null;
ImplementsClause implementsClause = null;
{
ITypeBinding[] superInterfaces = anoClassDeclaration.resolveBinding().getInterfaces();
if (superInterfaces.length != 0) {
superTypeBinding = superInterfaces[0];
TypeName superType = typeName(superInterfaces[0].getName());
putReference(superTypeBinding, (SimpleIdentifier) superType.getName());
implementsClause = implementsClause(superType);
} else {
TypeName superType = translateTypeName(superTypeBinding);
putReference(superTypeBinding, (SimpleIdentifier) superType.getName());
extendsClause = extendsClause(superType);
}
}
ClassDeclaration innerClass = classDeclaration(
null,
identifier(innerClassName),
extendsClause,
null,
implementsClause,
null);
artificialUnitDeclarations.add(innerClass);
if (extendsClause != null) {
List<FormalParameter> parameters = Lists.newArrayList();
List<Expression> arguments = Lists.newArrayList();
// find "super" constructor
IMethodBinding superConstructor = null;
for (IMethodBinding superMethod : superTypeBinding.getDeclaredMethods()) {
if (superMethod.isConstructor()) {
if (isSuperConstructor(superMethod, constructorBinding)) {
superConstructor = superMethod;
// additional parameters
for (int i = 0; i < additionalParameters.length / 2; i++) {
parameters.add(simpleFormalParameter(
null,
typeName(additionalParameters[2 * i + 0]),
additionalParameters[2 * i + 1]));
arguments.add(identifier(additionalParameters[2 * i + 1]));
}
// "declared" parameters
ITypeBinding[] parameterTypes = superMethod.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
TypeName dartParameterType = typeName(parameterTypes[i].getName());
String parameterName = "arg" + i;
parameters.add(simpleFormalParameter(dartParameterType, parameterName));
arguments.add(identifier(parameterName));
}
// done, we found and processed "super" constructor
break;
}
}
}
// declare "inner" constructor
FormalParameterList parameterList = formalParameterList(parameters);
ArgumentList argList = new ArgumentList(null, arguments, null);
SuperConstructorInvocation superCI = new SuperConstructorInvocation(null, null, null, argList);
context.getConstructorDescription(superConstructor).superInvocations.add(superCI);
ConstructorDeclaration innerConstructor = constructorDeclaration(
null,
null,
identifier(innerClassName),
null,
parameterList,
ImmutableList.<ConstructorInitializer> of(superCI),
emptyFunctionBody());
if (superTypeBinding.isEnum()) {
innerConstructor.setConstKeyword(token(Keyword.CONST));
}
innerClass.getMembers().add(innerConstructor);
}
for (Object javaBodyDeclaration : anoClassDeclaration.bodyDeclarations()) {
ClassMember classMember = translate((org.eclipse.jdt.core.dom.ASTNode) javaBodyDeclaration);
innerClass.getMembers().add(classMember);
}
return innerClass;
}
private String deleteLineContaining(String string, int index, boolean includePreceeding) {
int first = index;
while (!isEol(string.charAt(first))) {
first--;
}
if (includePreceeding) {
first = backupOverBlankLine(string, first);
}
int last = index;
while (!isEol(string.charAt(last))) {
last++;
}
return string.substring(0, first + 1) + string.substring(last);
}
private String deleteLinesContaining(String string, String substring, boolean includePreceeding) {
int index = string.indexOf(substring);
while (index >= 0) {
string = deleteLineContaining(string, index, includePreceeding);
index = string.indexOf(substring);
}
return string;
}
/**
* Set {@link #result} and return <code>false</code> - we don't want normal JDT visiting.
*/
private boolean done(AstNode node) {
result = node;
return false;
}
/**
* Convert #getPrefixes() to getPrefixes.
*/
private String extractLinkReference(String ref) {
// convert #fooBar() ==> fooBar
if (ref.startsWith("#")) {
ref = ref.substring(1);
}
int index = ref.indexOf('(');
if (index != -1) {
ref = ref.substring(0, index);
}
// convert 'Source source' ==> 'source'
index = ref.indexOf(' ');
if (index != -1) {
ref = ref.substring(0, index);
}
return ref;
}
/**
* There is a bug in the JDT, sometimes it produces wrong offsets for comments.
*/
private String fixJdtCommentLine(String line) {
line = StringUtils.stripEnd(line, null);
String trimmed = line.trim();
if (!trimmed.startsWith("//") && !trimmed.startsWith("/*")) {
StringBuilder sb = new StringBuilder();
int i = 0;
while (i < line.length()) {
char c = line.charAt(i++);
if (Character.isWhitespace(c)) {
sb.append(c);
} else if (c == '/') {
return sb.toString() + "//" + line.substring(i);
} else {
return sb.toString() + "// " + c + line.substring(i);
}
}
}
return line;
}
private String getJavaSource(org.eclipse.jdt.core.dom.ASTNode node) {
int offset = node.getStartPosition();
return javaSource.substring(offset, offset + node.getLength());
}
private boolean isEnum(Expression expression) {
ITypeBinding typeBinding = context.getNodeTypeBinding(expression);
if (typeBinding != null) {
return JavaUtils.isSubtype(typeBinding, "java.lang.Enum");
}
return false;
}
private boolean isEol(char character) {
return character == '\r' || character == '\n';
}
private boolean isNumberOrNull(Expression expression) {
if (expression instanceof IntegerLiteral || expression instanceof BooleanLiteral
|| expression instanceof DoubleLiteral || expression instanceof NullLiteral) {
return true;
}
ITypeBinding typeBinding = context.getNodeTypeBinding(expression);
if (typeBinding != null) {
String name = JavaUtils.getFullyQualifiedName(typeBinding, false);
return name.equals("boolean") || name.equals("byte") || name.equals("char")
|| name.equals("short") || name.equals("int") || name.equals("long")
|| name.equals("float") || name.equals("double") || name.equals("java.lang.Class");
}
return false;
}
private void putReference(org.eclipse.jdt.core.dom.IBinding binding, SimpleIdentifier identifier) {
if (binding != null) {
binding = JavaUtils.getOriginalBinding(binding);
context.putReference(identifier, binding, JavaUtils.getJdtSignature(binding));
}
}
private SimpleIdentifier replaceEnclosingClassMemberReferences(final ClassDeclaration innerClass,
final ITypeBinding enclosingTypeBinding) {
final SimpleIdentifier enclosingTypeInstRef;
final SimpleIdentifier enclosingTypeNameRef;
final AtomicBoolean addEnclosingTypeRef = new AtomicBoolean();
{
if (enclosingTypeBinding != null) {
String enclosingTypeName = enclosingTypeBinding.getName();
enclosingTypeInstRef = identifier(enclosingTypeName + "_this");
enclosingTypeNameRef = identifier(enclosingTypeName);
// add enclosing class references
innerClass.accept(new RecursiveAstVisitor<Void>() {
@Override
public Void visitMethodInvocation(MethodInvocation node) {
Expression target = node.getTarget();
if (target == null || target instanceof ThisExpression) {
IMethodBinding methodBinding = (IMethodBinding) context.getNodeBinding(node);
// TODO(scheglov) check also super classes
if (methodBinding != null
&& JavaUtils.isSubtype(enclosingTypeBinding, methodBinding.getDeclaringClass())) {
addEnclosingTypeRef.set(true);
node.setTarget(enclosingTypeInstRef);
}
}
return super.visitMethodInvocation(node);
}
@Override
public Void visitPropertyAccess(PropertyAccess node) {
node.getTarget().accept(this);
return null;
}
@Override
public Void visitSimpleIdentifier(SimpleIdentifier node) {
if (node.getParent() instanceof PrefixedIdentifier) {
return null;
}
Object binding = context.getNodeBinding(node);
if (binding instanceof IVariableBinding) {
IVariableBinding variableBinding = (IVariableBinding) binding;
if (variableBinding.isField()
&& variableBinding.getDeclaringClass() == enclosingTypeBinding) {
addEnclosingTypeRef.set(true);
if (JavaUtils.isStatic(variableBinding)) {
replaceNode(node.getParent(), node, propertyAccess(enclosingTypeNameRef, node));
} else {
replaceNode(node.getParent(), node, propertyAccess(enclosingTypeInstRef, node));
}
}
}
return super.visitSimpleIdentifier(node);
}
@Override
public Void visitThisExpression(ThisExpression node) {
ITypeBinding binding = context.getNodeTypeBinding(node);
if (JavaUtils.isSubtype(enclosingTypeBinding, binding)) {
addEnclosingTypeRef.set(true);
replaceNode(node.getParent(), node, enclosingTypeInstRef);
}
return super.visitThisExpression(node);
}
});
} else {
enclosingTypeInstRef = null;
}
}
if (!addEnclosingTypeRef.get()) {
return null;
}
return enclosingTypeInstRef;
}
/**
* Recursively translates given {@link org.eclipse.jdt.core.dom.ASTNode} to Dart {@link AstNode}.
*
* @return the corresponding Dart {@link AstNode}, may be <code>null</code> if <code>null</code>
* argument was given; not <code>null</code> if argument is not <code>null</code> (if
* translation is not implemented, exception will be thrown).
*/
@SuppressWarnings("unchecked")
private <T extends AstNode> T translate(final org.eclipse.jdt.core.dom.ASTNode node) {
if (node == null) {
return null;
}
ExecutionUtils.runRethrow(new RunnableEx() {
@Override
public void run() throws Exception {
Method method = getMostSpecificMethod(node.getClass());
try {
method.invoke(SyntaxTranslator.this, node);
} catch (InvocationTargetException e) {
ExecutionUtils.propagate(e.getCause());
}
}
});
if (result == null) {
System.out.println(node);
}
Assert.isNotNull(result, "No result for: " + node.getClass().getCanonicalName());
T castedResult = (T) result;
// remember type for each Expression
if (node instanceof org.eclipse.jdt.core.dom.Expression) {
context.putNodeTypeBinding(
result,
((org.eclipse.jdt.core.dom.Expression) node).resolveTypeBinding());
}
// attach leading comments to Statement
if (node instanceof org.eclipse.jdt.core.dom.Statement) {
int index = javaUnit.firstLeadingCommentIndex(node);
if (index != -1) {
List<String> commentLines = Lists.newArrayList();
List<org.eclipse.jdt.core.dom.Comment> allComments = javaUnit.getCommentList();
while (index < allComments.size()) {
org.eclipse.jdt.core.dom.Comment comment = allComments.get(index++);
if (comment.getStartPosition() > node.getStartPosition()) {
break;
}
String commentLine = getJavaSource(comment);
commentLine = fixJdtCommentLine(commentLine);
commentLines.add(commentLine);
}
result.setProperty(ToFormattedSourceVisitor.COMMENTS_BEFORE_STATEMENT, commentLines);
}
}
// attach comments at the end of Block
if (node instanceof org.eclipse.jdt.core.dom.Block) {
org.eclipse.jdt.core.dom.Block block = (org.eclipse.jdt.core.dom.Block) node;
int blockEnd = block.getStartPosition() + block.getLength();
// prepare the offset after last statement of after Block start
int afterOffset;
{
List<?> statements = block.statements();
if (statements.isEmpty()) {
afterOffset = block.getStartPosition();
} else {
int lastIndex = statements.size() - 1;
org.eclipse.jdt.core.dom.Statement lastStatement = (org.eclipse.jdt.core.dom.Statement) statements.get(lastIndex);
afterOffset = lastStatement.getStartPosition() + lastStatement.getLength();
}
}
// find comments at the end of Block
List<String> commentLines = Lists.newArrayList();
List<org.eclipse.jdt.core.dom.Comment> allComments = javaUnit.getCommentList();
int index = 0;
while (index < allComments.size()) {
org.eclipse.jdt.core.dom.Comment comment = allComments.get(index++);
if (comment.getStartPosition() < afterOffset) {
continue;
}
if (comment.getStartPosition() > blockEnd) {
break;
}
String commentLine = getJavaSource(comment);
commentLine = fixJdtCommentLine(commentLine);
commentLines.add(commentLine);
}
// remember comments
if (!commentLines.isEmpty()) {
result.setProperty(ToFormattedSourceVisitor.COMMENTS_AT_BLOCK_END, commentLines);
}
}
// done
result = null;
return castedResult;
}
private void translateAnnotations(AstNode dartNode, List<?> modifiers) {
for (Object modifier : modifiers) {
if (modifier instanceof org.eclipse.jdt.core.dom.Annotation) {
org.eclipse.jdt.core.dom.Annotation annotation = (org.eclipse.jdt.core.dom.Annotation) modifier;
String name = ((org.eclipse.jdt.core.dom.SimpleName) annotation.getTypeName()).getIdentifier();
ParsedAnnotation parsedAnnotation = new ParsedAnnotation(name);
if (modifier instanceof MarkerAnnotation) {
// no values
} else if (modifier instanceof SingleMemberAnnotation) {
SingleMemberAnnotation singleMemberAnnotation = (SingleMemberAnnotation) modifier;
org.eclipse.jdt.core.dom.Expression javaExpression = singleMemberAnnotation.getValue();
Expression dartExpression = translate(javaExpression);
Object value = dartExpression.accept(new ConstantEvaluator());
parsedAnnotation.put("value", value);
} else if (modifier instanceof NormalAnnotation) {
NormalAnnotation normalAnnotation = (NormalAnnotation) modifier;
for (Object javaPairObject : normalAnnotation.values()) {
MemberValuePair javaPair = (MemberValuePair) javaPairObject;
String pairName = javaPair.getName().getIdentifier();
org.eclipse.jdt.core.dom.Expression javaPairExpr = javaPair.getValue();
Expression dartExpression = translate(javaPairExpr);
Object value = dartExpression.accept(new ConstantEvaluator());
if (value == ConstantEvaluator.NOT_A_CONSTANT) {
value = dartExpression.toSource();
}
parsedAnnotation.put(pairName, value);
}
}
context.putNodeAnnotation(dartNode, parsedAnnotation);
}
}
}
/**
* Translates given {@link List} of {@link org.eclipse.jdt.core.dom.Expression} to the Dart
* {@link Expression} list.
*/
private List<Expression> translateArguments(IMethodBinding binding, List<?> javaArguments) {
List<Expression> arguments = translateExpressionList(javaArguments);
// may be some of the arguments are var-args
if (binding != null && binding.isVarargs()) {
int numRequired = binding.getParameterTypes().length - 1;
List<Expression> vars = Lists.newArrayList();
for (int i = numRequired; i < arguments.size(); i++) {
vars.add(arguments.get(i));
}
List<Expression> newArguments = Lists.newArrayList();
newArguments.addAll(arguments.subList(0, numRequired));
newArguments.add(new ListLiteral(null, null, null, vars, null));
arguments = newArguments;
}
// done
return arguments;
}
private List<ClassMember> translateBodyDeclarations(List<?> javaBodyDeclarations) {
List<ClassMember> members = Lists.newArrayList();
for (Iterator<?> I = javaBodyDeclarations.iterator(); I.hasNext();) {
org.eclipse.jdt.core.dom.BodyDeclaration javaBodyDecl = (org.eclipse.jdt.core.dom.BodyDeclaration) I.next();
constructorImpl = null;
if (javaBodyDecl instanceof org.eclipse.jdt.core.dom.TypeDeclaration
|| javaBodyDecl instanceof org.eclipse.jdt.core.dom.EnumDeclaration) {
ClassDeclaration innerClassDeclaration = translate(javaBodyDecl);
artificialUnitDeclarations.add(innerClassDeclaration);
} else {
ClassMember member = translate(javaBodyDecl);
members.add(member);
if (constructorImpl != null) {
members.add(constructorImpl);
}
}
}
return members;
}
private Expression translateExpression(Object o) {
return (Expression) translate((org.eclipse.jdt.core.dom.ASTNode) o);
}
/**
* Translates given {@link List} of {@link org.eclipse.jdt.core.dom.Expression} to the
* {@link List} of {@link Expression}s.
*/
private List<Expression> translateExpressionList(List<?> javaArguments) {
List<Expression> arguments = Lists.newArrayList();
for (Iterator<?> I = javaArguments.iterator(); I.hasNext();) {
org.eclipse.jdt.core.dom.Expression javaArg = (org.eclipse.jdt.core.dom.Expression) I.next();
Expression dartArg = translate(javaArg);
arguments.add(dartArg);
}
return arguments;
}
private Comment translateJavadoc(org.eclipse.jdt.core.dom.BodyDeclaration node) {
return (Comment) translate(node.getJavadoc());
}
/**
* Translates the JDT method declaration which is the constructor.
*/
private boolean translateMethodDeclarationConstructor(
org.eclipse.jdt.core.dom.MethodDeclaration node, IMethodBinding binding,
FormalParameterList parameterList, FunctionBody body,
SuperConstructorInvocation superConstructorInvocation) {
boolean isEnumConstructor = node.getParent() instanceof org.eclipse.jdt.core.dom.EnumDeclaration;
// prepare initializers
List<ConstructorInitializer> initializers = Lists.newArrayList();
if (superConstructorInvocation != null) {
initializers.add(superConstructorInvocation);
}
// prepare names
String technicalConstructorName = context.generateTechnicalConstructorName();
String constructorDeclName = technicalConstructorName + "_decl";
context.getConstructorDescription(binding).declName = constructorDeclName;
SimpleIdentifier nameNode = identifier(constructorDeclName);
// if enum, include implicit "name" and "ordinal" parameters
if (isEnumConstructor) {
context.getConstructorDescription(binding).isEnum = true;
List<FormalParameter> parameters = Lists.newArrayList();
parameters.add(simpleFormalParameter(typeName("String"), "name"));
parameters.add(simpleFormalParameter(typeName("int"), "ordinal"));
parameters.addAll(parameterList.getParameters());
parameterList = formalParameterList(parameters);
initializers = Lists.<ConstructorInitializer> newArrayList(superConstructorInvocation(
identifier("name"),
identifier("ordinal")));
}
// done
ConstructorDeclaration constructor = constructorDeclaration(
translateJavadoc(node),
identifier(node.getName().getIdentifier()),
nameNode,
parameterList,
initializers,
body);
if (isEnumConstructor) {
constructor.setConstKeyword(token(Keyword.CONST));
}
context.putConstructorBinding(constructor, binding);
translateAnnotations(constructor, node.modifiers());
return done(constructor);
}
/**
* Translates the parameters of the JDT method declaration into the Dart
* {@link FormalParameterList}.
*/
private FormalParameterList translateMethodDeclarationParameters(
org.eclipse.jdt.core.dom.MethodDeclaration node) {
FormalParameterList parameterList;
List<FormalParameter> parameters = Lists.newArrayList();
for (Iterator<?> I = node.parameters().iterator(); I.hasNext();) {
org.eclipse.jdt.core.dom.SingleVariableDeclaration javaParameter = (org.eclipse.jdt.core.dom.SingleVariableDeclaration) I.next();
SimpleFormalParameter parameter = translate(javaParameter);
translateAnnotations(parameter, javaParameter.modifiers());
parameters.add(parameter);
}
parameterList = formalParameterList(parameters);
return parameterList;
}
private SimpleIdentifier translateSimpleName(org.eclipse.jdt.core.dom.SimpleName name) {
return translate(name);
}
private TypeName translateTypeName(ITypeBinding binding) {
if (binding != null) {
if (binding.isArray()) {
return typeName(identifier("List"), translateTypeName(binding.getComponentType()));
}
String name = binding.getName();
name = StringUtils.substringBefore(name, "<");
if (JavaUtils.isTypeNamed(binding, "java.util.ArrayList")) {
name = "List";
}
if (JavaUtils.isTypeNamed(binding, "java.lang.Void")) {
name = "Object";
}
if ("boolean".equals(name)) {
return typeName("bool");
}
List<TypeName> arguments = Lists.newArrayList();
for (ITypeBinding typeArgument : binding.getTypeArguments()) {
arguments.add(translateTypeName(typeArgument));
}
TypeName result = typeName(identifier(name), arguments);
context.putNodeTypeBinding(result, binding);
context.putNodeTypeBinding(result.getName(), binding);
putReference(binding, (SimpleIdentifier) result.getName());
return result;
}
throw new IllegalArgumentException("" + binding);
}
/**
* Translates given {@link List} of {@link org.eclipse.jdt.core.dom.Type} to the
* {@link TypeArgumentList}.
*/
private List<TypeName> translateTypeNames(List<?> javaTypes) {
List<TypeName> typeNames = Lists.newArrayList();
for (Iterator<?> I = javaTypes.iterator(); I.hasNext();) {
org.eclipse.jdt.core.dom.Type javaType = (org.eclipse.jdt.core.dom.Type) I.next();
TypeName dartType = translate(javaType);
typeNames.add(dartType);
}
return typeNames;
}
/**
* Translates given {@link List} of {@link org.eclipse.jdt.core.dom.VariableDeclarationFragment}
* to the {@link VariableDeclarationList}.
*/
private VariableDeclarationList translateVariableDeclarationList(boolean isFinal,
org.eclipse.jdt.core.dom.Type javaType, List<?> javaVars) {
List<VariableDeclaration> variableDeclarations = Lists.newArrayList();
for (Iterator<?> I = javaVars.iterator(); I.hasNext();) {
org.eclipse.jdt.core.dom.VariableDeclarationFragment javaFragment = (org.eclipse.jdt.core.dom.VariableDeclarationFragment) I.next();
VariableDeclaration var = translate(javaFragment);
variableDeclarations.add(var);
}
return variableDeclarationList(
isFinal ? Keyword.FINAL : null,
(TypeName) translate(javaType),
variableDeclarations);
}
}